package org.wsiz.vro.web.converter;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.convert.ConversionService;
import org.springframework.data.domain.Persistable;
import org.springframework.data.repository.CrudRepository;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

/**
 * Base JSF converter for JPA entities.
 */
public class GenericConverter<E extends Persistable<PK>, PK extends Serializable> implements Converter {
    private Class<?> pkType = (Class<?>) ((ParameterizedType) getClass().getGenericSuperclass())
            .getActualTypeArguments()[1];

    private String printExpression = "id";
    @Autowired
    protected ConversionService conversionService;
    protected CrudRepository<E, PK> crudRepository;

    protected GenericConverter(CrudRepository<E, PK> crudRepository, String printExpression) {
        this.crudRepository = crudRepository;
        if (printExpression != null) {
            this.printExpression = printExpression;
        }
    }

    // -------------------------------
    // Converter implementation
    // -------------------------------

    @SuppressWarnings("unchecked")
    public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
        if (value == null || "-1".equals(value)) {
            return null;
        }

        PK id = (PK) conversionService.executeConversion(value, pkType);
        if (id != null) {
            return crudRepository.findOne(id);
        }

        return null;
    }

    @SuppressWarnings("unchecked")
    public String getAsString(FacesContext context, UIComponent component, Object object) {
        if (object == null || !Persistable.class.isAssignableFrom(object.getClass())) {
            return "-1";
        }
        return ((Persistable<PK>) object).getId().toString();
    }

    // ------------------------------------
    // Printer (used from view and flows)
    // ------------------------------------

    /**
     * Execute the printExpression using the passed entity as root object.
     */
    public String print(E entity) {
        if (entity == null) {
            return "";
        }
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(printExpression);
        return exp.getValue(entity, String.class);
    }
}
